package discovery

import (
	"encoding/json"
	"github.com/goinggo/mapstructure"
	log "github.com/sirupsen/logrus"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"time"
)

type MeshInfo struct {
	ThomasAddresses []string
	AgentInfo       Agent
	CacheAdrress    map[string]string
}

var (
	// todo修改不为数组用于高可用
	DefaultThomasAddress = "http://10.5.200.88:9992"
	CacheAdrress         = make(map[string]string)
	AgentInfo            Agent
	DiscoveryHttpClient  = http.Client{
		Transport:     nil,
		CheckRedirect: nil,
		Jar:           nil,
		Timeout:       10 * time.Second,
	}
)

func RegisterAgentFixTime(d time.Duration) {
	c := time.NewTicker(d)
	for range c.C {
		go func() {
			defer func() {
				if e := recover(); e != nil {
					log.Errorf("recover:%v", e)
				}
			}()
			RegisterAgent()
			RegisterWithConagent()
			// 缓存部分需要的服务
			for k, _ := range CacheAdrress {
				FetchAndCache(k)
			}
		}()
	}
}

func DiscoveryService(serviceName string) string {
	// 0. 缓存命中，如果命中直接返回
	if CacheAdrress[serviceName] != "" {
		return CacheAdrress[serviceName]
	}
	// 如果未命中直接依次从如下途径找
	address := FetchAndCache(serviceName)
	return address
}

func FetchAndCache(serviceName string) string {
	// 1. 环境变量中找
	serverAddress := os.Getenv(serviceName)
	if serverAddress != "" {
		CacheAdrress[serviceName] = serverAddress
		return serverAddress
	}
	// 2. agent配置中找
	if AgentInfo.ProxyServices != nil {
		for _, proxy := range AgentInfo.ProxyServices {
			if proxy.ServiceName == serviceName {
				// TODO 取一下http的接口
				for _, port := range proxy.Ports {
					serverAddress := proxy.Host + ":" + strconv.Itoa(int(port.Port))
					CacheAdrress[serviceName] = serverAddress
					return serverAddress
				}
			}
		}
	}
	// 3 注册中心找
	resp, error := DiscoveryHttpClient.Get(GetThomasUrl() + "/applications/" + serviceName)
	if error != nil || &resp == nil || resp.StatusCode != 200 {
		log.Errorf("error: %v resp:%v, status:%v", error, resp, resp.Status)
		return ""
	}
	if &resp != nil {
		defer resp.Body.Close()
	}
	body, _ := ioutil.ReadAll(resp.Body)
	var response WebResponse
	json.Unmarshal(body, &response)
	var app Application
	mapstructure.Decode(response.Data, &app)
	if response.Ret < 0 {
		return ""
	}
	serverAddress = app.GetAddress()
	log.Infof("%s address:%s", serviceName, serverAddress)
	if serverAddress != "" {
		CacheAdrress[serviceName] = serverAddress
	}
	return serverAddress
}

func (app Application) GetAddress() string {
	// TODO 负载均衡
	if &app == nil || len(app.Instances) == 0 {
		return ""
	}
	port := app.Instances[0].Port
	host := app.Instances[0].Addresses[0].Address
	return host + ":" + strconv.Itoa(port)
}

func GetThomasUrl() string {
	var thomas = os.Getenv("thomas")
	if thomas == "" {
		thomas = DefaultThomasAddress
	}
	return thomas
}

type Instance struct {
	Project        string
	AppId          string
	Cluster        string
	Dc             string
	ServiceId      string
	Addresses      []Address
	Port           int
	Protocol       string
	Meta           map[string]string
	Tags           []string
	Weigth         int
	PackageVersion string
	SrcVersion     string
	Status         string
	Timeout        int64
	CreateTime     int64
	UpdateTime     int64
	Version        int
}

type Address struct {
	Address string
	Type    string
	Meta    map[string]string
}

type WebResponse struct {
	Data    interface{}
	Error   ErrorStatus
	Ret     int
	BizCode int
}

type ErrorStatus struct {
	ErrorCode int
	msg       string
}

type Application struct {
	Service   string
	Instances []Instance
}

type ConagentRegisterReq struct {
	AppId          string
	Port           int32
	Protocol       string
	Check          CheckInfo
	Tags           []string
	PackageVersion string

	// 与conagent差别，新增如下字段
	Cluster string
	Project string
	Dc      string
}

type CheckInfo struct {
	Type            string
	Http            string
	Method          string
	Interval        int32
	Timeout         int32
	DeregisterAfter int32
}
